"""
https://github.com/offu/WeRoBot/issues/486
腾讯的限制
[BUG] handle里面函数处理超过5秒会连续调用3次
"""

import werobot
import datetime
import sys
import os
import time
import redis

sys.path.insert(0, '/home/peiyp/code')
from PyCmpltrtok.common import sep, to_decode, to_int
from python_nlp.xfaiss.flask_faiss_on_mongo_test import search
from python_nlp.kg.neo4j.seek_data_with_embed.xdiagnose import get_symptom, diagnose
from neo4j import GraphDatabase

"""
映射search函数需要的S_PORT到本机。
映射neo4j服务到本机N_PORT
"""
S_PORT = int(os.environ['S_PORT'])

# 连接neo4j
print('Connecting neo4j ...')
driver = GraphDatabase.driver(**{
    "uri": f"bolt://127.0.0.1:{os.environ['N_PORT']}",
    "auth": ("neo4j", "p1983ncbdA1-ei"),
    "encrypted": False
})
print('Connected to neo4j.')

# 连接Redis
rdb = redis.Redis('127.0.0.1', 6379, 0)
rdb.get('try_it')

robot = werobot.WeRoBot(token='kx8M7eODU4HiyAmw3a')
robot.config['APP_ID'] = 'wx47577a029b258f6b'
robot.config['ENCODING_AES_KEY'] = 'LjBrnwSvqGXx89Tszleo4sMYp8yMALcTArwhPjxoZGJ'
client = robot.client

CHAR_LIMIT = 450
PROMPT = '（输入“,或，”清空历史。诊断结果仅供参考，请咨询专业医师。）'
RESET_SET = set([',', '，'])
WAIT_LAST_TURN = '（请等待上一轮对话完成。）'
THRESH_S = 20  # 单例锁最多等待这么多秒
DIS_THRESH = 20.0  # 搜索时距离上限
WORDS_LIMIT = 4  # 搜索后最多用这么多症状


def list2str_with_limit(xlist, xlimit):
    """
    以xlimit个字符为限制，把列表转字符串。
    """
    assert(isinstance(xlimit, int) and xlimit > 0)
    xstr = ''
    xfirst = True
    for xelement in xlist:
        if len(xstr) + 2 + len(xelement) > xlimit:
            return xstr
        if xfirst:
            xfirst = False
        else:
            xstr += ', '
        xstr += str(xelement)
    return xstr


def encode_set(xset):
    """
    编码集合
    """
    if not xset:
        return ''
    return '|'.join(sorted(xset))


def decode_set(xstr):
    """
    解码编码后的集合。（注意返回是列表。）
    """
    if not xstr:
        return []
    return xstr.split('|')


@robot.handler
def hello(message):
    
    def do_return(xoutput):
        print('OUTPUT:', xoutput)
        # 解除单例限制
        rdb.srem('users_now', xuserid)
        return xoutput
    
    sep()
    
    # 判断类型
    xtype = message.type
    print(datetime.datetime.now(), xtype)
    if 'text' != xtype:
        return '目前仅支持文本信息！'
    
    # 用户标识
    xuserid = message.source
    print('userid:', type(xuserid), xuserid)

    # 输入文本
    xinput = message.content.strip()
    print('input:', xinput)

    # 单例限制
    xis_in = rdb.sismember('users_now', xuserid)
    ts_now = time.time_ns()
    if xis_in:
        ts = rdb.hget('user2ts', xuserid)
        ts = to_int(ts)
        if ts and ts_now - ts < THRESH_S * 1e9:
            xoutput = WAIT_LAST_TURN + PROMPT
            print('OUTPUT:', xoutput)
            return xoutput
    rdb.hset('user2ts', xuserid, ts_now)
    rdb.sadd('users_now', xuserid)
    
    # 重新开始
    if xinput in RESET_SET:
        rdb.hdel('user2dis', xuserid)
        return do_return('重新开始！请输入您的症状。' + PROMPT)
    
    # 当前用户的结果
    xencoded = rdb.hget('user2dis', xuserid)
    xencoded = to_decode(xencoded)
    xdis_set = set(decode_set(xencoded))

    # 搜索输入，选出距离小于阈值的结果
    xoutput = search(xinput, 10, S_PORT)
    xsents = xoutput['sentences']
    xds = xoutput['D']
    print('候选:')
    xwords = []
    for i, s in enumerate(xsents):
        d = xds[i]
        print(i, s, d, d < DIS_THRESH)
        if d < DIS_THRESH:
            xwords.append(s)

    # 从这些结果中挑选出是“临床表现”的节点名字
    xlist = get_symptom(driver, xwords, WORDS_LIMIT)
    print(f'输入了症状：{xlist}')

    # 用这些临床表现去诊断
    xdiseases = diagnose(driver, xlist)
    if xdiseases is None:
        xoutput = '本轮没有找到相关疾病。'
    else:
        print(f'本轮诊断：{xdiseases}')
        if not xdis_set:
            # 第一次诊断
            xdis_set = set(xdiseases)
        else:
            # 后续诊断
            xresult = xdis_set.intersection(set(xdiseases))  # 求交集
            if len(xresult):
                print('【有交集】')
            else:
                # 没有交集求并集
                print('【没有交集求并集】')
                xresult = xdis_set.union(set(xdiseases))
            xdis_set = xresult
        xdis_list = sorted(xdis_set)
        xoutput = '目前的诊断是：' + list2str_with_limit(xdis_list, CHAR_LIMIT)

    # 保存用户结果
    xencoded = encode_set(xdis_set)
    rdb.hset('user2dis', xuserid, xencoded.encode('utf8'))

    return do_return(xoutput + PROMPT)


# 让服务器监听在 0.0.0.0:80
robot.config['HOST'] = '0.0.0.0'
robot.config['PORT'] = 80
robot.run()
